#
#   Enemy.py
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; version 2 of the License.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   Copyright
#   Author: Nicholas F. Hoover
#   Contributors: Salvatore S. Gionfriddo
#   Created: 2007.06.25
#   Last Modified: 2007.11.07
#

import random, pygame
from Character import Character
from Animation import Animation

class Enemy(Character):

##############
# ANIMATIONS #
##############

    NUM_ANIMATIONS = 5
    (IDLE,
     WALK_UP,
     WALK_DOWN,
     WALK_SIDE,
     ATTACK1) = range(NUM_ANIMATIONS)  
    
    LEFT = NUM_ANIMATIONS

    ###############
    # FRAME TICKS #
    ###############
    IDLE_ANIMATION_FRAME_TICKS = 1
    WALK_ANIMATION_FRAME_TICKS = 2
    ATTACK1_ANIMATION_FRAME_TICKS = 7
    
    ##############
    # KEY FRAMES #
    ##############
    ATTACK1_KEY_FRAME = 1
      
    DEFAULT_ANIMATION = IDLE

    
    ###################
    # ANIMATION TABLE #
    ###################
    ANIMATION_TABLE = [
        (IDLE, 'BasicKid_Run',  IDLE_ANIMATION_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_UP, 'BasicKid_Run',  WALK_ANIMATION_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_DOWN, 'BasicKid_Run',  WALK_ANIMATION_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_SIDE, 'BasicKid_Run',  WALK_ANIMATION_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (ATTACK1,     'BasicKid_Bite', ATTACK1_ANIMATION_FRAME_TICKS, ATTACK1_KEY_FRAME)]


#########
# STATS #    
#########  
    SPEED = 4
    MAX_HEALTH = 100
    ATTACK1_DAMAGE = 5
    
########
# MISC #
########
    TARGET_REACHED_X_RANGE = 5
    TARGET_REACHED_Y_RANGE = 4

    DEFAULT_POSITION = (0,0)
    RECT_SIZE = (30,50)

    COLLISION_RECT_SIZE = (10,2)
    COLLISION_RECT_POSITION = (10,48)
    
    GROUPABLE = True
    GROUPER = False
    GROUP_ATTACH_RANGE = 20
    
    PARACHUTING = False
    
    CHANCE_TO_THROW = 1 # one in one-thousand
    CAN_THROW = False

    ACTIONS = [ATTACK1]

    AVERT = True
    LOCKED_TO_SCREEN = False
    
    def __init__(self):
        self.throwing = False
        self.grouped = False
        self.speed_bonus = 0
        
        self.MOVEMENTS = [[0,0],[0,-self.SPEED],[0,self.SPEED],[self.SPEED,0],[0,0]]
        Character.__init__(self)
        Character.reset(self)
        self.nextAction = self.IDLE
        self.nextFacing = self.facing
        
        self.behavior = Behavior()

    # AI Sub Functions

    def doMovement(self, collision, targetRect):
        facing = self.facing

        xRange = 0
        xRangeR = (targetRect.centerx - Enemy.TARGET_REACHED_X_RANGE) - self.rect.right
        xRangeL = self.rect.left - (targetRect.centerx + Enemy.TARGET_REACHED_X_RANGE)
        
        if xRangeR > 0:
            xwalk = self.WALK_SIDE
            facing = self.RIGHT
            xRange = xRangeR
            xSpeed = self.SPEED
        elif xRangeL > 0:
            xwalk = self.WALK_SIDE
            facing = self.LEFT
            xRange = xRangeL
            xSpeed = -self.SPEED
        else:
            xSpeed = 0
            
        yRange = 0
        yRangeD = (targetRect.bottom - Enemy.TARGET_REACHED_Y_RANGE) - self.rect.bottom
        yRangeU = self.rect.bottom - (targetRect.bottom + Enemy.TARGET_REACHED_Y_RANGE)
        
        if yRangeU > 0: 
            ywalk = self.WALK_UP
            yRange = yRangeU
            ySpeed = -self.SPEED
        elif yRangeD > 0:
            ywalk = self.WALK_DOWN
            yRange = yRangeD
            ySpeed = self.SPEED
        else:
            ySpeed = 0

        self.MOVEMENTS[self.WALK_UP] = [xSpeed, ySpeed]
        self.MOVEMENTS[self.WALK_DOWN] = [xSpeed, ySpeed]
        self.MOVEMENTS[self.WALK_SIDE] = [self.SPEED, ySpeed]

        if xRange == 0 and yRange == 0:
            #self.attack1()
            pass
        else:
            if xRange > yRange: walk = xwalk
            else: walk = ywalk
            if walk != self.nextAction or facing != self.facing:
                self.walk(walk, facing)

    def stalk(self, collision, targetRect):
        self.doMovement(collision, targetRect)

    def getFleeRect(self, tRect):
        dx = self.rect.left - tRect.left
        dy = self.rect.bottom - tRect.bottom
        
        fRect = pygame.Rect(self.rect)
        fRect.left += dx
        fRect.bottom += dy
        
        return fRect
    
    def getWanderRect(self, tRect):
        fRect = pygame.Rect(tRect)
        
        if self.rect.left >= 400 and self.rect.bottom >= 300:   (x,y) = (700,150)
        elif self.rect.left >= 400 and self.rect.bottom <= 300: (x,y) = (200,50)
        elif self.rect.left <= 400 and self.rect.bottom >= 300: (x,y) = (600,550)
        elif self.rect.left <= 400 and self.rect.bottom <= 300: (x,y) = (100,450)
        else: (x,y) = (0,0)

        fRect.left = x
        fRect.bottom = y
        
        return fRect
        
    def wander(self, collision, targetRect):
        targetRect = self.getWanderRect(targetRect)
        self.doMovement(collision, targetRect)
        
    def flee(self, collision, targetRect):
        #change target
        targetRect = self.getFleeRect(targetRect)
        #do movement
        self.doMovement(collision, targetRect)
        
    def attack(self):
        self.attack1()
        
    def throw(self):
        self.throwing = True
        
    # status checks
    def inAttackRange(self, targetRect):
        (xRange, yRange) = self.rangeFromTarget(targetRect)
        return (xRange == 0 and yRange == 0)
        
    def inThrowRange(self, targetRect):
        (xRange, yRange) = self.rangeFromTarget(targetRect)
        return (self.CAN_THROW and (xRange > (2*self.TARGET_REACHED_X_RANGE) or yRange > (2*self.TARGET_REACHED_Y_RANGE)) and self.CHANCE_TO_THROW >= random.randint(0,1000))
        
    def rangeFromTarget(self, targetRect):
        xRangeR = (targetRect.centerx - Enemy.TARGET_REACHED_X_RANGE) - self.rect.right # R rC : (C-r) - R
        xRangeL = self.rect.left - (targetRect.centerx + Enemy.TARGET_REACHED_X_RANGE)  # Cr L : L - (C+r)
        
        yRangeD = (targetRect.bottom - Enemy.TARGET_REACHED_Y_RANGE) - self.rect.bottom # 
        yRangeU = self.rect.bottom - (targetRect.bottom + Enemy.TARGET_REACHED_Y_RANGE) # 
        
        xRange = max([xRangeR, xRangeL, 0])
        yRange = max([yRangeD, yRangeU, 0])
        
        return (xRange, yRange)
        
    def getKidStatus(self, collision):
        return (0,len(collision))
        
    def performAI(self, collision, targetRect):
        if self.PARACHUTING:
            self.MOVE = False
            return
        else: self.MOVE = True
        
        #do ai
        (deadKids, livingKids) = self.getKidStatus(collision)
        behavior = self.behavior.getBehavior(deadKids, livingKids, self.inAttackRange(targetRect), self.inThrowRange(targetRect))
        if behavior == Behavior.WANDER:
            self.wander(collision, targetRect)
        elif behavior == Behavior.STALK:
            self.stalk(collision, targetRect)
        elif behavior == Behavior.FLEE:
            self.flee(collision, targetRect)
        elif behavior == Behavior.THROW:
            self.throw()
        elif behavior == Behavior.ATTACK:
            self.attack()
            
class Behavior:
    NUM_BEHAVIORS = 5
    (WANDER, STALK, FLEE, THROW, ATTACK) = range(NUM_BEHAVIORS)
    
    MIN_AGGRESSION = 0
    MAX_AGGRESSION = 10
    
    MIN_FEAR = 0
    MAX_FEAR = 10
    
    NUM_PERSONALITIES = 3
    (WUSSY, NORMAL, AGGRESSIVE) = range(NUM_PERSONALITIES)
    
    def __init__(self, personality = NORMAL):
        self.aggression = 5 + personality
        self.fear = 5
        self.wander_time = 0
        self.personality = personality
        
        self.current_behavior = Behavior.WANDER
        self.behavior_time = 0
    
    def getBehavior(self, deadKidsNearBy, livingKidsNearBy, inAttackRange, inThrowRange):
        self.modifyState(deadKidsNearBy, livingKidsNearBy)
        behavior = Behavior.WANDER
        if inAttackRange:
            behavior = Behavior.ATTACK
        elif inThrowRange:
            behavior = Behavior.THROW
        else:
            if self.fear > self.aggression:
                behavior = Behavior.FLEE
            elif self.aggression > self.fear:
                behavior = Behavior.STALK
            else:
                behavior = Behavior.WANDER
        if self.current_behavior == behavior: self.behavior_time += 1
        else:
            self.behavior_time = 0
            self.current_behavior = behavior
            #print behavior
        return behavior
    
    def modifyState(self, deadKidsNearBy, livingKidsNearBy):
        timeInBehavior = 100
        if self.current_behavior == Behavior.WANDER:
            if self.behavior_time > timeInBehavior:
                self.aggression += 1
            else:
                pass
        elif self.current_behavior == Behavior.FLEE:
            if self.behavior_time > timeInBehavior:
                self.aggression += 1
            else:
                pass
        elif self.current_behavior == Behavior.THROW:
            if self.behavior_time > timeInBehavior:
                self.fear += 1
            else:
                self.fear = 5
                self.aggression = 5
                
        #self.aggression += self.personality
                
        if self.fear > 10: self.fear = 10
        elif self.fear < 0: self.fear = 0
        if self.aggression > 10: self.aggression = 10
        elif self.aggression < 0: self.aggression = 0
    
